Skip to content

fix(dashboard): aggregate paired-device usage in the desktop Dashboard and menubar badge - #866

Merged
iamtoruk merged 2 commits into
getagentseal:mainfrom
marcreynolds:feat/desktop-combined-scope
Aug 3, 2026
Merged

fix(dashboard): aggregate paired-device usage in the desktop Dashboard and menubar badge#866
iamtoruk merged 2 commits into
getagentseal:mainfrom
marcreynolds:feat/desktop-combined-scope

Conversation

@marcreynolds

Copy link
Copy Markdown
Contributor

Summary

Fixes #795. The desktop app's Dashboard showed only the local device's
cost
, while the menu bar statistics and the web GUI reported usage
aggregated across paired devices. This makes the desktop Dashboard consistent
with those surfaces by adding a Local / Combined scope control, mirroring
the macOS menu bar's existing Scope setting.

Root cause: the desktop spawned codeburn status --format menubar-json
without --scope combined, so the CLI never ran its paired-device
aggregation and the renderer only ever had local data.

What changed

Desktop app

  • New Scope: Local / Combined setting in Settings › General (persists to
    localStorage, default Local), mirroring the menu bar.
  • Combined passes --scope combined to the CLI, forces the provider filter to
    all, and clears the Claude-config scope (the CLI rejects --scope combined
    alongside a provider/project filter).
  • The Overview hero shows the paired-device aggregate (cost / calls /
    sessions) with a "Combined · N devices" caption and a per-device
    breakdown when Combined is selected.

Menu bar

  • The badge figure now reflects Combined scope instead of always showing the
    local total: it fetches the combined payload for the badge's period and
    renders the cross-device aggregate, falling back to local when no combined
    payload is available (cold cache, or an unreachable peer).

Scope / limitations

  • Only the hero KPIs aggregate; the detailed panels (daily chart, model table,
    etc.) remain local, because the combined payload carries totals only — this
    matches the menu bar's behavior.
  • Aggregation is peer-to-peer over the LAN: a paired device contributes only
    while it is reachable. An unreachable peer degrades gracefully to the
    reachable subset.

Testing

  • Desktop: npm run typecheck and npm run build:electron clean; npm test → 445 passing (+9 new covering the scope argv, the Settings control,
    and the combined hero).
  • Menu bar: swift build clean; new AppStore scope tests added (run
    under the Xcode toolchain: swift test).
  • End-to-end: verified against the live CLI on a real two-device setup —
    Combined reported the aggregate ($163.54 month) vs local-only ($95.94),
    matching the menu bar and web GUI.

… badge

Fixes getagentseal#795: the desktop Dashboard showed only local-device cost while the
menubar and web GUI aggregated across paired devices.

Desktop app:
- Add a Local/Combined Scope setting (Settings > General), mirroring the
  macOS menubar. Combined passes `--scope combined` to the CLI, forces the
  provider filter to all, and persists to localStorage.
- Overview hero shows the paired-device aggregate (cost/calls/sessions) with
  a "Combined · N devices" caption and per-device breakdown when Combined is
  selected; detailed panels remain local (the combined payload carries totals
  only).

Menubar:
- The badge figure now reflects Combined scope instead of always showing the
  local total: refreshMenubarBadge fetches the combined payload for the badge
  period and the badge renders the cross-device aggregate, falling back to
  local when no combined payload is available.
@iamtoruk
iamtoruk marked this pull request as ready for review August 3, 2026 19:33

@iamtoruk iamtoruk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes #795. Verified end to end. Root cause was exactly as described: the desktop spawned status --format menubar-json without --scope combined, so the CLI never ran paired-device aggregation. The fix wires a Local/Combined scope control through buildOverviewArgs (combined passes --scope combined and drops the provider filter, which the CLI rejects alongside it; the renderer forces provider='all' in that mode so nothing is lost), and makes the menubar badge fetch the combined payload best-effort — success is gated only on the local fetch, so a slow or unreachable peer degrades gracefully to the local figure rather than failing the badge.

Local scope (the default) is untouched. On the merged tree (main was 29 ahead, clean merge): tsc clean, desktop npm test 462/462, swift test 128 tests + all XCTest suites at 0 failures, including the new scope-persistence, combined-total, and local-fallback cases. Merging as the base for #867.

@iamtoruk
iamtoruk merged commit bb506e0 into getagentseal:main Aug 3, 2026
iamtoruk added a commit that referenced this pull request Aug 3, 2026
…he all-provider view

buildDurablePeriod derived the today slice of the multi-day, all-provider
headline from the unsliced whole-range parse, so a turn spanning local midnight
kept its category and turn count anchored on its yesterday start. The per-call
cost and calls bucketed onto today correctly, but By Activity and the JSON
daily turn count lost the post-midnight half — categories summed to only the
pre-midnight cost while the headline, By Model and By Project were right.

Slice the today parse with filterProjectsByDays first, which re-anchors the
straddling turn to its surviving today calls, so today's category cost lands on
today. Category cost is the sum of the slice's own calls, so day-N + day-N+1
still equals the whole-range total (no over-count); the per-day turn-count
split matches the cache side and the documented per-day semantics.

Adds a regression test in the straddling-turn conservation suite
(mutation-checked: fails on the pre-fix code). Also fills in the CHANGELOG
Unreleased entries for the batch (#853, #856, #872, #846/#859, #866/#867, #833).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop app dashboard shows only local device costs while paired devices are aggregated elsewhere

2 participants